﻿using System;
using System.Diagnostics;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// </summary>
    internal class GameClock
    {
        #region Static Operations

        /// <summary>
        /// 	Gets the frequency of the timer as the number of ticks per second. This field is read-only.
        /// </summary>
        internal static long Frequency
        {
            get
            {
                return Stopwatch.Frequency;
            }
        }

        /// <summary>
        /// 	Gets the current number of ticks in the timer mechanism.
        /// </summary>
        internal static long Counter
        {
            get
            {
                return Stopwatch.GetTimestamp();
            }
        }

        #region Private Methods

        private static TimeSpan CounterToTimeSpan(long delta)
        {
            var ticks = (delta * TimeSpan.TicksPerSecond) / Frequency;
            return TimeSpan.FromTicks(ticks);
        }

        #endregion

        #endregion

        #region Fields & Properties

        private long _baseRealTime;
        private long _lastRealTime;
        private long _lastRealTimeCandidate;
        private bool _lastRealTimeValid;

        private int _suspendCount;
        private long _suspendStartTime;
        private long _timeLostToSuspension;

        private TimeSpan _currentTimeBase;
        private TimeSpan _currentTimeOffset;

        /// <summary>
        /// </summary>
        internal TimeSpan CurrentTime
        {
            get
            {
                return (_currentTimeBase + _currentTimeOffset);
            }
        }

        private TimeSpan _elapsedTime;

        /// <summary>
        /// </summary>
        internal TimeSpan ElapsedTime
        {
            get
            {
                return _elapsedTime;
            }
        }

        private TimeSpan _elapsedAdjustedTime;

        /// <summary>
        /// </summary>
        internal TimeSpan ElapsedAdjustedTime
        {
            get
            {
                return _elapsedAdjustedTime;
            }
        }

        #endregion

        #region Constructors & Destructors

        /// <summary>
        /// </summary>
        public GameClock()
        {
            Reset();
        }

        #endregion

        #region Run

        /// <summary>
        /// </summary>
        internal void Reset()
        {
            _currentTimeBase = TimeSpan.Zero;
            _currentTimeOffset = TimeSpan.Zero;
            _baseRealTime = Counter;
            _lastRealTimeValid = false;
        }

        /// <summary>
        /// </summary>
        internal void Resume()
        {
            _suspendCount--;
            if (_suspendCount <= 0)
            {
                _timeLostToSuspension += Counter - _suspendStartTime;
                _suspendStartTime = 0L;
            }
        }

        /// <summary>
        /// </summary>
        internal void Suspend()
        {
            _suspendCount++;
            if (_suspendCount == 1)
            {
                _suspendStartTime = Counter;
            }
        }

        /// <summary>
        /// </summary>
        internal void AdvanceFrameTime()
        {
            _lastRealTime = _lastRealTimeCandidate;
            _timeLostToSuspension = 0L;
        }

        /// <summary>
        /// </summary>
        internal void UpdateElapsedTime()
        {
            var counter = Counter;
            if (!_lastRealTimeValid)
            {
                _lastRealTime = counter;
                _lastRealTimeValid = true;
            }
            try
            {
                _currentTimeOffset = CounterToTimeSpan(counter - _baseRealTime);
            }
            catch (OverflowException)
            {
                _currentTimeBase += _currentTimeOffset;
                _baseRealTime = _lastRealTime;
                try
                {
                    _currentTimeOffset = CounterToTimeSpan(counter - _baseRealTime);
                }
                catch (OverflowException)
                {
                    _baseRealTime = counter;
                    _currentTimeOffset = TimeSpan.Zero;
                }
            }
            try
            {
                _elapsedTime = CounterToTimeSpan(counter - _lastRealTime);
            }
            catch (OverflowException)
            {
                _elapsedTime = TimeSpan.Zero;
            }
            try
            {
                var realTime = _lastRealTime + _timeLostToSuspension;
                _elapsedAdjustedTime = CounterToTimeSpan(counter - realTime);
            }
            catch (OverflowException)
            {
                _elapsedAdjustedTime = TimeSpan.Zero;
            }
            _lastRealTimeCandidate = counter;
        }

        #endregion
    }
}